package jm.demo.jaxb.permission;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * <p>Load SecurityContexts from <code>location</code>
 * 
 * <p>
 * 
 * @author jiming
 *
 */
public class SecurityContextManager implements InitializingBean, ApplicationContextAware, ISecurityContextManager {
	
	// public static final String CONTEXT_TYPE_CDNMGR = 
	
	public static final Random random = new Random();
	
	public static final String CONFIG_FILE = "config";
	
	private static ObjectMapper mapper = new ObjectMapper();
	
	static {
        AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
        mapper.getSerializationConfig().setAnnotationIntrospector(introspector);
        mapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
	}
	
	public static ObjectMapper getObjectMapper() {
		return mapper;
	}

	private String location;
	private Map<String, SecurityContextType> contexts;

	private ApplicationContext applicationContext;
	

	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getLocation()
	 */
	@Override
	public String getLocation() {
		return location;
	}

	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#setLocation(java.lang.String)
	 */
	@Override
	public void setLocation(String location) {
		this.location = StringUtils.trimWhitespace(location);
	}






	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getContexts()
	 */
	@Override
	public Map<String, SecurityContextType> getContexts() {
		return contexts;
	}

	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#setContexts(java.util.Map)
	 */
	@Override
	public void setContexts(Map<String, SecurityContextType> contexts) {
		this.contexts = contexts;
	}



	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getSecurityContext(java.lang.String)
	 */
	@Override
	public SecurityContextType getSecurityContext(String context_type) {
		return this.getContexts().get(context_type);
	}

	
	
	@Override
	public void setApplicationContext(ApplicationContext application_context)
			throws BeansException {
		this.applicationContext = application_context;			
	}
	
	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getApplicationContext()
	 */
	@Override
	public ApplicationContext getApplicationContext() {
		return this.applicationContext;
	}

	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getSecurityContextTypeInJSON(java.lang.String)
	 */
	@Override
	public String getSecurityContextTypeInJSON(String security_context_name) {
		SecurityContextType sc = this.getSecurityContext(security_context_name);
		if(sc == null) return "{}";
		
        try {
            return mapper.writeValueAsString(sc);
		} catch (IOException e) {
			return "{}";
		}

	}
	
	
	/* (non-Javadoc)
	 * @see jm.demo.jaxb.permission.ISecurityContextManager#getSecurityContextTypeRoles(java.lang.String, java.lang.String)
	 */
	@Override
	public List<Role> getSecurityContextTypeRoles(String context_type, String ... role_names) {
		SecurityContextType context = this.getSecurityContext(context_type);
		if(context == null) return Collections.emptyList();
		
		if(ObjectUtils.isEmpty(role_names)) {
			return context.getRoles();
		}
		
		List<Role> result = new ArrayList<Role>(role_names.length);
		for(Role role: context.getRoles()) {
			if(ObjectUtils.containsElement(role_names, role.getName())) {
				result.add(role);
			}
		}
		
		return result;		
	}


	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.hasText(this.getLocation(), "SecurityContextManager.location is required!");
		if(!this.getLocation().endsWith("/") || !this.getLocation().endsWith("\\")) {
			this.setLocation(this.getLocation() + "/");
		}
		
		contexts = new TreeMap<String, SecurityContextType>();
		
		Resource resource = this.getApplicationContext().getResource(this.getLocation() + CONFIG_FILE);
		@SuppressWarnings("unchecked")
		List<String> files = (List<String>)IOUtils.readLines(resource.getInputStream(), "UTF-8");
		for(String file_name: files) {
			if(file_name.startsWith("#")) { // ignore comment
				continue;
			}
			Resource res = this.getApplicationContext().getResource(this.getLocation() + file_name);
			Reader in = new InputStreamReader(res.getInputStream(), "utf-8");
			SecurityContextType security_context = SecurityContextType.load(in);
			this.contexts.put(security_context.getContextType(), security_context);
		}
	}


}
